﻿using BUGCOME.Infrastructure.Cache;
using BUGCOME.Infrastructure.Model;
using BUGCOME.Infrastructure;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

namespace BUGCOME.ServiceCore.Filters
{
    /// <summary>
    /// 授权校验过滤器特性
    /// 用于验证请求是否包含有效的身份认证信息（JWT令牌）
    /// 支持跳过授权验证（通过在控制器或操作方法上添加 [AllowAnonymous] 特性）
    /// </summary>
    [AttributeUsage(AttributeTargets.All)]
    public class VerifyAttribute : Attribute, IAuthorizationFilter
    {
        private NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

        /// <summary>
        /// 执行授权验证逻辑
        /// 验证请求的JWT令牌有效性，并在令牌即将过期时自动刷新
        /// </summary>
        /// <param name="context">授权过滤器上下文，包含请求和响应信息</param>
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            // 检查是否标记了 [AllowAnonymous] 特性，如有则跳过授权验证
            var noNeedCheck = false;
            if (context.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
            {
                noNeedCheck = controllerActionDescriptor.MethodInfo.GetCustomAttributes(inherit: true)
                    .Any(a => a.GetType().Equals(typeof(AllowAnonymousAttribute)));
            }

            if (noNeedCheck) return;

            // 记录请求信息（IP和URL）用于日志追踪
            string ip = HttpContextExtension.GetClientUserIp(context.HttpContext);
            string url = context.HttpContext.Request.Path;
            var isAuthed = context.HttpContext.User.Identity.IsAuthenticated;
            string osType = context.HttpContext.Request.Headers["os"];  // 获取客户端操作系统类型

            // 从请求中解析JWT令牌并获取登录用户信息
            TokenModel loginUser = JwtUtil.GetLoginUser(context.HttpContext);

            // 令牌存在且有效，处理令牌刷新逻辑
            if (loginUser != null)
            {
                var nowTime = DateTime.UtcNow;
                TimeSpan ts = loginUser.ExpireTime - nowTime;  // 计算令牌剩余有效期

                // 构建缓存键（用于避免短时间内重复刷新令牌）
                var cacheKey = "token_" + loginUser.UserId;

                // 当令牌即将过期（剩余不足5分钟）且尚未触发刷新缓存时，执行令牌刷新
                if (!CacheHelper.Exists(cacheKey) && ts.TotalMinutes < 5)
                {
                    // 生成新的JWT令牌
                    var newToken = JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(loginUser));

                    // 设置刷新缓存标记（有效期1分钟），防止短时间内重复刷新
                    CacheHelper.SetCache(cacheKey, cacheKey, 1);

                    // 移动端响应头处理（确保自定义头能被前端获取）
                    if (osType != null)
                    {
                        context.HttpContext.Response.Headers.Append("Access-Control-Expose-Headers", "X-Refresh-Token");
                    }

                    // 记录令牌刷新日志（用户ID和新令牌）
                    logger.Info($"刷新token,userName={loginUser.UserName},token={newToken}");

                    // 在响应头中添加新令牌，前端可据此更新本地令牌
                    context.HttpContext.Response.Headers.Append("X-Refresh-Token", newToken);
                }
            }

            // 令牌无效或用户未认证，返回401未授权错误
            if (loginUser == null || !isAuthed)
            {
                string msg = $"请求访问[{url}]失败，无法访问系统资源";
                //logger.Info(msg);  // 生产环境可能需要记录未授权访问日志
                context.Result = new JsonResult(ApiResult.Error(ResultCode.DENY, msg));
            }
        }
    }
}
